using System;
using System.Collections;
using System.Collections.Specialized;


namespace CreateScript
{
	/// <summary>
	/// Class to parse command arguments, categorizing unique switches and their arguments.
	/// </summary>
	/// <remarks>
	/// This utility class parses a string array as a set of command arguments, and classifies
	/// those arguments as switches and optional associated value arguments.  The general command
	/// syntax is presumed to be:
	/// 
	///		[[-/]switchname [value]*]*
	///		
	///	where switchname is the name of the switch (any number of characters) and value is an argument.
	///	
	///	Note that either "-" or "/" can be used to prefix a switch.  Multiple occurrences of the same
	///	switch will have all of their values grouped together.  By default, the switchname is case-insensitive,
	///	but that can be overridden on the ArgumentParser constructor.
	///	
	///	Once constructed, individual, unique switches can be accessed via the Switch collection
	///	(ArgumentParser.ArgumentSwitches, derived from ArrayList), either indexed by switch name, or
	///	accessed by one of the other inherited ArrayList mechanisms (foreach, Count, etc.).  Each
	///	ArgumentSwitch object so obtained has a Name property and a StringCollection of Values.
	///	
	///	Example:
	///	
	///	If a command line to a program is:
	///	
	///			-f file1 file2 -ident "This is the identifier" /F file3
	///			
	///	and the program creates an ArgumentParser named argParser from the command arguments passed to Main():
	///	
	///			ArgumentParser argParser = new ArgumentParser(args);
	///	
	///	then the following conditions are all true:
	///	
	///			argParser.Switch.Count == 2
	///			argParser.Switch["f"].Values.Count == 3
	///			argParser.Switch["f"].Values[0] == "file1"
	///			argParser.Switch["f"].Values[1] == "file2"
	///			argParser.Switch["f"].Values[2] == "file3"
	///			argParser.Switch["ident"].Values.Count == 1
	///			argParser.Switch["ident"].Values[0] == "This is the identifier"
	///			argParser.Switch["xyzzy"] == null
	///			
	///	Note that because we did not specify case-sensitivity, "-f" and "/F" are both considered to be the
	///	same switch.  Note also that enclosing a command line argument in quotes keeps it from being parsed
	///	as separate arguments.
	///			
	/// </remarks>
	public class ArgumentParser
	{
		/// <summary>
		/// Class that describes a unique switch and its arguments.
		/// </summary>
		public class ArgumentSwitch
		{
			private string m_name;				// Unique name of this switch
			public StringCollection Values;		// Collection of arguments to the switch

			public ArgumentSwitch() : this("")	// Default constructor, empty name
			{
			}

			public ArgumentSwitch(string name)	// Constructor with name specified
			{
				m_name = name;
				Values = new StringCollection();
			}

			public string Name					// Read-only Name property
			{
				get { return m_name; }
			}

         public string Value
         {
            get
            {
               string val = null;

               if (null != Values  &&  Values.Count > 0)
               {
                  val = Values[0];
               }

               return (string.IsNullOrEmpty(val) ? string.Empty : val);
            }
         }
		}

		/// <summary>
		/// Collection of ArgumentSwitch objects (derived from ArrayList)
		/// </summary>
		public class ArgumentSwitches : ArrayList
		{
			private bool m_bCaseSensitive;		// Are switch names case-sensitive?

			public ArgumentSwitches() : this(false)	// Default constructor (not case-sensitive)
			{
			}

			public ArgumentSwitches(bool bCaseSensitive) : base()	// Constructor with case-sensitivity specified
			{
				m_bCaseSensitive = bCaseSensitive;
			}

			public ArgumentSwitch this[string name]	// Indexer by switch name (get only)
			{
				get
				{
					ArgumentSwitch rv = null;		// If not found, we will return a null object reference
					string target;
					if (m_bCaseSensitive)			// If case-sensitive
						target = name;				// Use specified name excactly
					else
						target = name.ToLower();	// Convert to lower-case for case-insensitive search
					foreach (ArgumentSwitch sw in this)	// Use the ArrayList iteration
					{
						if (sw.Name == target)		// Is this what we're looking for?
						{
							rv = sw;				// Yes
							break;					// exit the loop
						}
					}
					return rv;
				}
			}

         public string GetArgumentSwitchValue(string name)
         {
            ArgumentSwitch sw = this[name];
            return (null == sw ? string.Empty : sw.Value);
         }


			/// <summary>
			/// Read-only property for case-sensitivity
			/// </summary>
			public bool CaseSensitive
			{
				get { return m_bCaseSensitive; }
			}
		}

		/// <summary>
		/// Collection of ArgumentSwitch objects
		/// </summary>
		public ArgumentSwitches Switch;

		/// <summary>
		/// Default constructor (empty argument list, case-insensitive)
		/// </summary>
		public ArgumentParser() : this(new string[0], false)
		{
		}

		/// <summary>
		/// Constructor with argument list, but default case-sensitivity
		/// </summary>
		/// <param name="args">Argument array</param>
		public ArgumentParser(string[] args) : this(args, false)
		{
		}

		/// <summary>
		/// Constructor with argument list and case-sensitivity specified
		/// </summary>
		/// <param name="args">Argument array</param>
		/// <param name="bCaseSensitive">Case-sensitivity</param>
		public ArgumentParser(string[] args, bool bCaseSensitive)
		{
			Switch = new ArgumentSwitches(bCaseSensitive);	// Initialize switch collection
			AddArguments(args);								// Parse the arguments
		}

		/// <summary>
		/// Method for parsing an argument array and adding switches/values to the Switch collection.
		/// </summary>
		/// <remarks>
		/// Note that this method can be called more than once, and the results are cumulative.
		/// </remarks>
		/// <param name="args">Argument array</param>
		public void AddArguments(string[] args)
		{
			ArgumentSwitch sw = null;						// Current switch being processed
			foreach (string arg in args)					// Iterate through the arguments
			{
				switch (arg.Substring(0,1))					// Check the first character
				{
					case "-":								// Is it a switch?
					case "/":
						string switchName = arg.Substring(1);	// Get the name
						sw = Switch[switchName];			// Do we have this one already?
						if (sw == null)						// No, so add it
						{
							if (!Switch.CaseSensitive)		// Convert to lowercase if case-insensitive
								switchName = switchName.ToLower();
							sw = new ArgumentSwitch(switchName);	// Create the new switch object
							Switch.Add(sw);					// Add it to the collection
						}
						break;

					default:								// Not a switch, must be a value argument
						if (sw != null)						// Unless we have not yet seen a switch
							sw.Values.Add(arg);				// This is an argument to the last switch encountered
						break;
				}
			}
		}

		/// <summary>
		/// Read-only property for case-sensitivity
		/// </summary>
		public bool CaseSensitive
		{
			get { return Switch.CaseSensitive; }
		}
	}
}
